(function ($) {
    function setSize(container) {
        var state = $.data(container, "accordion");
        var opts = state.options;
        var panels = state.panels;
        var cc = $(container);
        opts.fit ? $.extend(opts, cc._fit()) : cc._fit(false);
        if (!isNaN(opts.width)) {
            cc._outerWidth(opts.width);
        } else {
            cc.css("width", "");
        }
        var headerHeight = 0;
        var bodyHeight = "auto";
        var headers = cc.find(">div.panel>div.accordion-header");
        if (headers.length) {
            headerHeight = $(headers[0]).css("height", "")._outerHeight();
        }
        
        if (!isNaN(opts.height)) {
            cc._outerHeight(opts.height);
            bodyHeight = cc.height() - headerHeight * headers.length;
            // 需求号：2777670，手风琴展开状态底部遮盖问题，差一象素
            var isLiteStyle = false;
            if ("undefined" != typeof HISUIStyleCode && HISUIStyleCode.toLocaleLowerCase() == 'lite') isLiteStyle = true;
            if (isLiteStyle) {
                bodyHeight -= 1;
            }
        } else {
            cc.css("height", "");
        }
        /*
            2018-09-27 add by wanghc 
            accordion-gray ==> margin-top:4px;
        */
        if (cc.hasClass('accordion-gray')){
            bodyHeight -= 4 * (headers.length-1)+1;  // totalHeight - margin-top * (accordionCount-1)
        }
        _resize(true, bodyHeight - _resize(false) + 1);
        function _resize(collapsible, height) {
            var totalHeight = 0;
            for (var i = 0; i < panels.length; i++) {
                var p = panels[i];
                var h = p.panel("header")._outerHeight(headerHeight);
                if (p.panel("options").collapsible == collapsible) {
                    var pheight = isNaN(height) ? undefined : (height + headerHeight * h.length);
                    p.panel("resize", { width: cc.width(), height: (collapsible ? pheight : undefined) });
                    totalHeight += p.panel("panel").outerHeight() - headerHeight;
                }
            }
            return totalHeight;
        };
    };
    function findBy(container, property, value, all) {
        var panels = $.data(container, "accordion").panels;
        var pp = [];
        for (var i = 0; i < panels.length; i++) {
            var p = panels[i];
            if (property) {
                if (p.panel("options")[property] == value) {
                    pp.push(p);
                }
            } else {
                if (p[0] == $(value)[0]) {
                    return i;
                }
            }
        }
        if (property) {
            return all ? pp : (pp.length ? pp[0] : null);
        } else {
            return -1;
        }
    };
    function getSelections(container) {
        return findBy(container, "collapsed", false, true);
    };
    function getSelected(container) {
        var pp = getSelections(container);
        return pp.length ? pp[0] : null;
    };
    function getPanelIndex(container, panel) {
        return findBy(container, null, panel);
    };
    function getPanel(container, which) {
        var panels = $.data(container, "accordion").panels;
        if (typeof which == "number") {
            if (which < 0 || which >= panels.length) {
                return null;
            } else {
                return panels[which];
            }
        }
        return findBy(container, "title", which);
    };
    function setProperties(container) {
        var opts = $.data(container, "accordion").options;
        var cc = $(container);
        if (opts.border) {
            cc.removeClass("accordion-noborder");
        } else {
            cc.addClass("accordion-noborder");
        }
    };
    function init(container) {
        var state = $.data(container, "accordion");
        var cc = $(container);
        cc.addClass("accordion");
        state.panels = [];
        cc.children("div").each(function () {
            var opts = $.extend({}, $.parser.parseOptions(this), { selected: ($(this).attr("selected") ? true : undefined) });
            var pp = $(this);
            state.panels.push(pp);
            createPanel(container, pp, opts);
        });
        cc.bind("_resize", function (e, force) {
            var opts = $.data(container, "accordion").options;
            if (opts.fit == true || force) {
                setSize(container);
            }
            return false;
        });
    };
    function createPanel(container, pp, options) {
        var opts = $.data(container, "accordion").options;
        pp.panel($.extend({}, { collapsible: true, minimizable: false, maximizable: false, closable: false, doSize: false, collapsed: true, headerCls: "accordion-header", bodyCls: "accordion-body" }, options, {
            onBeforeExpand: function () {
                if (options.onBeforeExpand) {
                    if (options.onBeforeExpand.call(this) == false) {
                        return false;
                    }
                }
                if (!opts.multiple) {
                    var all = $.grep(getSelections(container), function (p) {
                        return p.panel("options").collapsible;
                    });
                    for (var i = 0; i < all.length; i++) {
                        unselect(container, getPanelIndex(container, all[i]));
                    }
                }
                var header = $(this).panel("header");
                header.addClass("accordion-header-selected");
                header.find(".accordion-collapse").removeClass("accordion-expand");
            }, onExpand: function () {
                if (options.onExpand) {
                    options.onExpand.call(this);
                }
                opts.onSelect.call(container, $(this).panel("options").title, getPanelIndex(container, this));
            }, onBeforeCollapse: function () {
                if (options.onBeforeCollapse) {
                    if (options.onBeforeCollapse.call(this) == false) {
                        return false;
                    }
                }
                var header = $(this).panel("header");
                header.removeClass("accordion-header-selected");
                header.find(".accordion-collapse").addClass("accordion-expand");
            }, onCollapse: function () {
                if (options.onCollapse) {
                    options.onCollapse.call(this);
                }
                opts.onUnselect.call(container, $(this).panel("options").title, getPanelIndex(container, this));
            }
        }));
        var header = pp.panel("header");
        var tool = header.children("div.panel-tool");
        tool.children("a.panel-tool-collapse").hide();
        var t = $("<a href=\"javascript:void(0)\"></a>").addClass("accordion-collapse accordion-expand").appendTo(tool);
        t.bind("click", function () {
            var index = getPanelIndex(container, pp);
            if (pp.panel("options").collapsed) {
                select(container, index);
            } else {
                unselect(container, index);
            }
            return false;
        });
        pp.panel("options").collapsible ? t.show() : t.hide();
        header.click(function () {
            $(this).find("a.accordion-collapse:visible").triggerHandler("click");
            return false;
        });
    };
    function select(container, which) {
        var p = getPanel(container, which);
        if (!p) {
            return;
        }
        stopAnimate(container);
        var opts = $.data(container, "accordion").options;
        p.panel("expand", opts.animate);
    };
    function unselect(container, which) {
        var p = getPanel(container, which);
        if (!p) {
            return;
        }
        stopAnimate(container);
        var opts = $.data(container, "accordion").options;
        p.panel("collapse", opts.animate);
    };
    function doFirstSelect(container) {
        var opts = $.data(container, "accordion").options;
        var p = findBy(container, "selected", true);
        if (p) {
            _select(getPanelIndex(container, p));
        } else {
            _select(opts.selected);
        }
        function _select(index) {
            var animate = opts.animate;
            opts.animate = false;
            select(container, index);
            opts.animate = animate;
        };
    };
    function stopAnimate(container) {
        var panels = $.data(container, "accordion").panels;
        for (var i = 0; i < panels.length; i++) {
            panels[i].stop(true, true);
        }
    };
    function add(container, options) {
        var state = $.data(container, "accordion");
        var opts = state.options;
        var panels = state.panels;
        if (options.selected == undefined) {
            options.selected = true;
        }
        stopAnimate(container);
        var pp = $("<div></div>").appendTo(container);
        panels.push(pp);
        createPanel(container, pp, options);
        setSize(container);
        opts.onAdd.call(container, options.title, panels.length - 1);
        if (options.selected) {
            select(container, panels.length - 1);
        }
    };
    function remove(container, which) {
        var state = $.data(container, "accordion");
        var opts = state.options;
        var panels = state.panels;
        stopAnimate(container);
        var panel = getPanel(container, which);
        var title = panel.panel("options").title;
        var index = getPanelIndex(container, panel);
        if (!panel) {
            return;
        }
        if (opts.onBeforeRemove.call(container, title, index) == false) {
            return;
        }
        panels.splice(index, 1);
        panel.panel("destroy");
        if (panels.length) {
            setSize(container);
            var curr = getSelected(container);
            if (!curr) {
                select(container, 0);
            }
        }
        opts.onRemove.call(container, title, index);
    };
    $.fn.accordion = function (options, param) {
        if (typeof options == "string") {
            return $.fn.accordion.methods[options](this, param);
        }
        options = options || {};
        return this.each(function () {
            var state = $.data(this, "accordion");
            if (state) {
                $.extend(state.options, options);
            } else {
                $.data(this, "accordion", { options: $.extend({}, $.fn.accordion.defaults, $.fn.accordion.parseOptions(this), options), accordion: $(this).addClass("accordion"), panels: [] });
                init(this);
            }
            setProperties(this);
            setSize(this);
            doFirstSelect(this);
        });
    };
    $.fn.accordion.methods = {
        options: function (jq) {
            return $.data(jq[0], "accordion").options;
        }, panels: function (jq) {
            return $.data(jq[0], "accordion").panels;
        }, resize: function (jq) {
            return jq.each(function () {
                setSize(this);
            });
        }, getSelections: function (jq) {
            return getSelections(jq[0]);
        }, getSelected: function (jq) {
            return getSelected(jq[0]);
        }, getPanel: function (jq, which) {
            return getPanel(jq[0], which);
        }, getPanelIndex: function (jq, panel) {
            return getPanelIndex(jq[0], panel);
        }, select: function (jq, which) {
            return jq.each(function () {
                select(this, which);
            });
        }, unselect: function (jq, which) {
            return jq.each(function () {
                unselect(this, which);
            });
        }, add: function (jq, options) {
            return jq.each(function () {
                add(this, options);
            });
        }, remove: function (jq, which) {
            return jq.each(function () {
                remove(this, which);
            });
        }
    };
    $.fn.accordion.parseOptions = function (target) {
        var t = $(target);
        return $.extend({}, $.parser.parseOptions(target, ["width", "height", { fit: "boolean", border: "boolean", animate: "boolean", multiple: "boolean", selected: "number" }]));
    };
    $.fn.accordion.defaults = {
        width: "auto", height: "auto", fit: false, border: true, animate: true, multiple: false, selected: 0, onSelect: function (title, index) {
        }, onUnselect: function (title, index) {
        }, onAdd: function (title, index) {
        }, onBeforeRemove: function (title, index) {
        }, onRemove: function (title, index) {
        }
    };
})(jQuery);